package pool;

import com.chen.common.exception.CodeThrow;
import com.chen.common.exception.Throw;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * @author ex_chenzy59
 * @since 2025/4/11
 */
@Slf4j
public class ObjectPool<T> {
    private static ScheduledThreadPoolExecutor executor;
    private final LinkedBlockingQueue<PoolObject<T>> idleObjects;
    private final LinkedBlockingQueue<PoolObject<T>> busyObjects;
    private final PoolObjectFactory<T> factory;
    /*池中最大空闲对象数*/
    private final int maxIdle;
    /*池中对象的最大数*/
    private final int maxTotal;
    /*获取新对象时的等待时长,单位秒,默认5秒,-1表示不等待,即不阻塞,暂未实现*/
    private final int maxWait;
    /*对象最大空闲时长,如果对象空闲时长达到该值,可能移除该对象*/
    private final int maxIdleDuration;

    /*每次最多驱逐数量: 参考BaseGenericObjectPool.numTestsPerEvictionRun*/
    private final int numPerEviction;
    /*驱逐线程任务执行间隔,单位秒,小于10秒就不会执行,参考BaseGenericObjectPool.timeBetweenEvictionRuns*/
    private final int evictionRunsDuration;

    /*池中最小空闲对象数*/
    private final int minIdle;
    /*在连接空闲时，是否进行有效性检查。如果设置为 true，则会进行检查，可以减少无效连接的积累*/
    private final boolean testWhileIdle;

    volatile boolean closed;
    /*驱逐空闲对象的任务*/
    private Evictor<T> evictor;
    public ObjectPool(PoolObjectFactory<T> factory,ObjectPoolConfig config){
        this.maxIdle=config.getMaxIdle();
        this.maxTotal=config.getMaxTotal();
        this.maxWait=config.getMaxWait();
        this.maxIdleDuration=config.getMaxIdleDuration();
        this.minIdle=config.getMinIdle();
        this.testWhileIdle= config.isTestWhileIdle();
        this.numPerEviction=config.getNumPerEviction();
        idleObjects=new LinkedBlockingQueue<>(maxIdle);
        busyObjects=new LinkedBlockingQueue<>(maxTotal);
        this.factory=factory;
        this.evictionRunsDuration=config.getEvictionRunsDuration();
        startEvictor(evictionRunsDuration);

    }

    /**
     * 驱逐任务
     * @param evictionRunsDuration 任务执行间隔,单位秒
     */
    private final void startEvictor(int evictionRunsDuration) {
        if (evictionRunsDuration > 0) {
            synchronized (ObjectPool.class) {
                evictor = new Evictor<>(this);
                schedule(evictor, evictionRunsDuration);
            }
        }
    }
    private synchronized void schedule(Evictor<T> task, int duration) {
        if (null == executor) {
            executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory());
            executor.setRemoveOnCancelPolicy(true);
        }
        executor.scheduleWithFixedDelay(task,duration,duration, TimeUnit.SECONDS);
    }

    /*校验池中对象是否都在使用中*/
    private boolean checkFullUsing(){
        return busyObjects.size()>=maxTotal;
    }
    public T borrowObject(){
         var poolObject=borrow();
         factory.activateObject(poolObject);
         return poolObject.get();
    }
    private PoolObject<T> borrow(){
        try {
            PoolObject<T> result= idleObjects.poll();
            if (result!=null){
                return result;
            }
            if (checkFullUsing()){
                Throw.error(CodeThrow.Exception_ObjectPool, "池中对象已到最大值:" + maxTotal + ",且所有对象都在使用中");
            }
            return create();
        } catch (Exception e) {
            throw Throw.errorGet(CodeThrow.Exception_ObjectPool,"对象池中拿出对象异常",e);
        }
    }


    private PoolObject<T> create(){
        var result = factory.makeObject();
        return result;
    }
    final void assertOpen() throws IllegalStateException {
        if (isClosed()) {
            throw new IllegalStateException("Pool not open");
        }
    }
    public final boolean isClosed() {
        return closed;
    }

    /**
     * 空闲队列中驱逐对象
     */
    public synchronized void evict() throws Exception {
        if (idleObjects.isEmpty()){
            return;
        }
        var iterator=idleObjects.iterator();
        /*需要驱逐的对象的数量*/
        /*思考: 是否像apache的GenericObjectPool一样,把getEvictNum放循环头部,每循环一次就调用一次getEvictNum获取最新的需要驱逐数量*/
        /*结论: 不能放到循环头部,不然会一直循环,直到所有对象都超过最大空闲时长,然后都驱逐*/
        var evictNum=getEvictNum();
        for (var i=0;i<evictNum;i++){
            if (!iterator.hasNext()){
                /*没有对象了,*/
                return;
            }
            var item=iterator.next();
            if (!item.startEvict()){
                /*不是空闲状态,可能是其他线程借用了*/
                continue;
            }
            if (idleObjects.size()>minIdle||item.isMaxIdle(maxIdleDuration)){
                destroy(item);
            }else {
                if (testWhileIdle){
                    /*校验对象是否可用*/
                    boolean active = false;
                    try {
                        factory.activateObject(item);
                        active = true;
                    } catch (final Exception e) {
                        destroy(item);
                    }
                    if (active) {
                        boolean validate;
                        try {
                            validate = factory.validateObject(item);
                        } catch (final Throwable t) {
                            log.error("验证对象有效性时异常:",t);
                            throw t;
                        }
                        if (validate) {
                            try {
                                factory.passivateObject(item);
                            } catch (final Exception e) {
                                log.error("校验通过后归还对象异常:",e);
                                destroy(item);
                            }
                        } else {
                            destroy(item);
                        }
                    }
                }
            }
        }
    }
    private void destroy(final PoolObject<T> toDestroy) throws Exception {
        toDestroy.invalidate();
        idleObjects.remove(toDestroy);
        try {
            factory.destroyObject(toDestroy);
        } finally {
//            destroyedCount.incrementAndGet();
//            createCount.decrementAndGet();
        }
    }

    /**
     * 得到需要驱逐的对象的数量
     * -1表示空闲数不大于最小空闲数,不需要驱逐
     */
    private int getEvictNum(){
        var idleExcludeMin=idleObjects.size()-minIdle;
        if (idleExcludeMin<=0){
            /*空闲数量小于等于[最小空闲数],需要校验空闲时长,所以不能返回idleExcludeMin*/
              return Math.min(idleObjects.size(),numPerEviction);
        }
        return Math.min(idleExcludeMin,numPerEviction);
    }
    private static class EvictorThreadFactory implements ThreadFactory {

        @Override
        public Thread newThread(final Runnable runnable) {
            final Thread thread = new Thread(null, runnable, "chen-pool-evictor");
            thread.setDaemon(true); //守护线程
            return thread;
        }
    }
    @Slf4j
    public static class Evictor<T> implements Runnable{
        private final ObjectPool<T> pool;

        public Evictor(ObjectPool<T> pool) {
            this.pool = pool;
        }

        @Override
        public void run() {
            log.debug("----------------------对象池驱逐任务开始执行----------------------");
            try {
                pool.evict();
            } catch (Exception e) {
                log.error("驱逐对象异常:",e);
                throw new RuntimeException(e);
            }finally {
                log.debug("----------------------对象池驱逐任务执行结束----------------------");
            }
        }
    }
}
